{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 证件图片分类器模型训练\n",
    "\n",
    "> 使用卷积神经网络模型进行证件图片分类，全套程序使用Python完成，主要的开发框架有Tensorflow、Keras、Numpy、Pillow。如需重新训练该模型，可在`'./datasets'`目录当中添加新的训练样本，然后点击菜单栏 Kernel -> Restart & Run All，完成训练后会自动将模型保存为`'model.h5'`文件。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 导入深度学习框架并开始构建神经网络"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "np.random.seed(3) # 固定初始随机值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/kk/.local/lib/python3.5/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.\n",
      "  from ._conv import register_converters as _register_converters\n",
      "Using TensorFlow backend.\n"
     ]
    }
   ],
   "source": [
    "import keras\n",
    "from keras.layers import (\n",
    "    Input, Conv2D, MaxPool2D, BatchNormalization, Flatten, Dense, Dropout, Activation)\n",
    "from keras.models import Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import os, pickle"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from libs.datasetFuncs import readDatasets, getDatas, getTargets"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 初始配置\n",
    "\n",
    "- `mini_img_shape` 通常不需要修改；\n",
    "- `classes_num` 指定类别的数量；\n",
    "- `batch_size` 训练批次大小，一般不需要改变；\n",
    "- `epochs_num` 训练轮数，可以根据实际情况调整，但不宜过大。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "USE_CLEAN = False\n",
    "USE_TRAINING = False\n",
    "\n",
    "mini_img_shape = (64, 64, 3) # 图片的（高，宽，rgb通道）\n",
    "\n",
    "batch_size = 128\n",
    "epochs_num = 60\n",
    "\n",
    "datasets_path = './datasets/'\n",
    "labels = os.listdir(datasets_path)\n",
    "classes_num = len(labels)\n",
    "model_filename = '../libs/doc_clf3.h5'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "input_img = Input(shape=mini_img_shape, name='input_img') # 输入"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对象&方法说明\n",
    "\n",
    "- Conv2D 二维卷积操作\n",
    "- MaxPool2D 最大值下采样\n",
    "- BatchNormalization 批标准化，标准化层间输入数据数值\n",
    "- relu 为激活方程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "TensorShape([Dimension(None), Dimension(6), Dimension(6), Dimension(20)])"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "conv1 = Conv2D(1, (3, 3), activation='relu', padding='same')(input_img)\n",
    "conv2 = Conv2D(5, (3, 3), activation='relu')(conv1)\n",
    "pool1 = MaxPool2D((2, 2))(conv2)\n",
    "\n",
    "normalized1 = BatchNormalization(axis=1)(pool1)\n",
    "conv3 = Conv2D(10, (3, 3), activation='relu')(normalized1)\n",
    "pool2 = MaxPool2D((2, 2))(conv3)\n",
    "conv4 = Conv2D(20, (3, 3), activation='relu')(pool2)\n",
    "pool4 = MaxPool2D((2, 2))(conv4)\n",
    "pool4.get_shape()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对象&方法说明\n",
    "\n",
    "- Flatten 用于将卷积后的值展开，方便全连接层的矩阵积操作\n",
    "- Dense 全连接层\n",
    "- softmax 为激活方程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "TensorShape([Dimension(None), Dimension(27)])"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "flattened = Flatten()(pool4)\n",
    "normalized2 = BatchNormalization(axis=-1)(flattened)\n",
    "dense1 = Dense(128, activation='relu')(normalized2)\n",
    "dropout = Dropout(0.5)(dense1)\n",
    "dense2 = Dense(classes_num, activation='softmax', name='output_vect')(dropout)\n",
    "dense2.get_shape()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "if not USE_CLEAN and os.path.isfile(model_filename):\n",
    "    model = keras.models.load_model(model_filename)\n",
    "else:\n",
    "    USE_CLEAN = True\n",
    "    model = Model(inputs=input_img, outputs=dense2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对象&方法说明\n",
    "\n",
    "- rmsprop 训练优化器的一种\n",
    "- categorical_crossentropy 多分类交叉商，常用于多分类模型训练\n",
    "- metrics=['accuracy'] 测试模型准确度"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "if USE_CLEAN:\n",
    "    model.compile(optimizer='rmsprop',\n",
    "                  loss='categorical_crossentropy',\n",
    "                  metrics=['accuracy'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 可视化神经网络结构\n",
    "\n",
    "**PS:** 使用该功能需安装 pydot 包，可以使用命令：`pip install pydot` 进行安装。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/svg+xml": [
       "<svg height=\"1134pt\" viewBox=\"0.00 0.00 434.00 1134.00\" width=\"434pt\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
       "<g class=\"graph\" id=\"graph0\" transform=\"scale(1 1) rotate(0) translate(4 1130)\">\n",
       "<title>G</title>\n",
       "<polygon fill=\"white\" points=\"-4,4 -4,-1130 430,-1130 430,4 -4,4\" stroke=\"none\"/>\n",
       "<!-- 140449680358144 -->\n",
       "<g class=\"node\" id=\"node1\"><title>140449680358144</title>\n",
       "<polygon fill=\"none\" points=\"69,-1079.5 69,-1125.5 357,-1125.5 357,-1079.5 69,-1079.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"130\" y=\"-1098.8\">InputX: InputLayer</text>\n",
       "<polyline fill=\"none\" points=\"191,-1079.5 191,-1125.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"218.5\" y=\"-1110.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"191,-1102.5 246,-1102.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"218.5\" y=\"-1087.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"246,-1079.5 246,-1125.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"301.5\" y=\"-1110.3\">(None, 64, 64, 3)</text>\n",
       "<polyline fill=\"none\" points=\"246,-1102.5 357,-1102.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"301.5\" y=\"-1087.3\">(None, 64, 64, 3)</text>\n",
       "</g>\n",
       "<!-- 140449680358256 -->\n",
       "<g class=\"node\" id=\"node2\"><title>140449680358256</title>\n",
       "<polygon fill=\"none\" points=\"68.5,-996.5 68.5,-1042.5 357.5,-1042.5 357.5,-996.5 68.5,-996.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"130\" y=\"-1015.8\">conv2d_1: Conv2D</text>\n",
       "<polyline fill=\"none\" points=\"191.5,-996.5 191.5,-1042.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"219\" y=\"-1027.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"191.5,-1019.5 246.5,-1019.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"219\" y=\"-1004.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"246.5,-996.5 246.5,-1042.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"302\" y=\"-1027.3\">(None, 64, 64, 3)</text>\n",
       "<polyline fill=\"none\" points=\"246.5,-1019.5 357.5,-1019.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"302\" y=\"-1004.3\">(None, 64, 64, 1)</text>\n",
       "</g>\n",
       "<!-- 140449680358144&#45;&gt;140449680358256 -->\n",
       "<g class=\"edge\" id=\"edge1\"><title>140449680358144-&gt;140449680358256</title>\n",
       "<path d=\"M213,-1079.37C213,-1071.15 213,-1061.66 213,-1052.73\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"216.5,-1052.61 213,-1042.61 209.5,-1052.61 216.5,-1052.61\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140449680358816 -->\n",
       "<g class=\"node\" id=\"node3\"><title>140449680358816</title>\n",
       "<polygon fill=\"none\" points=\"68.5,-913.5 68.5,-959.5 357.5,-959.5 357.5,-913.5 68.5,-913.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"130\" y=\"-932.8\">conv2d_2: Conv2D</text>\n",
       "<polyline fill=\"none\" points=\"191.5,-913.5 191.5,-959.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"219\" y=\"-944.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"191.5,-936.5 246.5,-936.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"219\" y=\"-921.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"246.5,-913.5 246.5,-959.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"302\" y=\"-944.3\">(None, 64, 64, 1)</text>\n",
       "<polyline fill=\"none\" points=\"246.5,-936.5 357.5,-936.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"302\" y=\"-921.3\">(None, 62, 62, 5)</text>\n",
       "</g>\n",
       "<!-- 140449680358256&#45;&gt;140449680358816 -->\n",
       "<g class=\"edge\" id=\"edge2\"><title>140449680358256-&gt;140449680358816</title>\n",
       "<path d=\"M213,-996.366C213,-988.152 213,-978.658 213,-969.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"216.5,-969.607 213,-959.607 209.5,-969.607 216.5,-969.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140449680358704 -->\n",
       "<g class=\"node\" id=\"node4\"><title>140449680358704</title>\n",
       "<polygon fill=\"none\" points=\"26.5,-830.5 26.5,-876.5 399.5,-876.5 399.5,-830.5 26.5,-830.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"130\" y=\"-849.8\">max_pooling2d_1: MaxPooling2D</text>\n",
       "<polyline fill=\"none\" points=\"233.5,-830.5 233.5,-876.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"261\" y=\"-861.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"233.5,-853.5 288.5,-853.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"261\" y=\"-838.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"288.5,-830.5 288.5,-876.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"344\" y=\"-861.3\">(None, 62, 62, 5)</text>\n",
       "<polyline fill=\"none\" points=\"288.5,-853.5 399.5,-853.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"344\" y=\"-838.3\">(None, 31, 31, 5)</text>\n",
       "</g>\n",
       "<!-- 140449680358816&#45;&gt;140449680358704 -->\n",
       "<g class=\"edge\" id=\"edge3\"><title>140449680358816-&gt;140449680358704</title>\n",
       "<path d=\"M213,-913.366C213,-905.152 213,-895.658 213,-886.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"216.5,-886.607 213,-876.607 209.5,-886.607 216.5,-886.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140449680384464 -->\n",
       "<g class=\"node\" id=\"node5\"><title>140449680384464</title>\n",
       "<polygon fill=\"none\" points=\"-2.84217e-14,-747.5 -2.84217e-14,-793.5 426,-793.5 426,-747.5 -2.84217e-14,-747.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"130\" y=\"-766.8\">batch_normalization_1: BatchNormalization</text>\n",
       "<polyline fill=\"none\" points=\"260,-747.5 260,-793.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"287.5\" y=\"-778.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"260,-770.5 315,-770.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"287.5\" y=\"-755.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"315,-747.5 315,-793.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"370.5\" y=\"-778.3\">(None, 31, 31, 5)</text>\n",
       "<polyline fill=\"none\" points=\"315,-770.5 426,-770.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"370.5\" y=\"-755.3\">(None, 31, 31, 5)</text>\n",
       "</g>\n",
       "<!-- 140449680358704&#45;&gt;140449680384464 -->\n",
       "<g class=\"edge\" id=\"edge4\"><title>140449680358704-&gt;140449680384464</title>\n",
       "<path d=\"M213,-830.366C213,-822.152 213,-812.658 213,-803.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"216.5,-803.607 213,-793.607 209.5,-803.607 216.5,-803.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140449680384744 -->\n",
       "<g class=\"node\" id=\"node6\"><title>140449680384744</title>\n",
       "<polygon fill=\"none\" points=\"65,-664.5 65,-710.5 361,-710.5 361,-664.5 65,-664.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"126.5\" y=\"-683.8\">conv2d_3: Conv2D</text>\n",
       "<polyline fill=\"none\" points=\"188,-664.5 188,-710.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"215.5\" y=\"-695.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"188,-687.5 243,-687.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"215.5\" y=\"-672.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"243,-664.5 243,-710.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"302\" y=\"-695.3\">(None, 31, 31, 5)</text>\n",
       "<polyline fill=\"none\" points=\"243,-687.5 361,-687.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"302\" y=\"-672.3\">(None, 29, 29, 10)</text>\n",
       "</g>\n",
       "<!-- 140449680384464&#45;&gt;140449680384744 -->\n",
       "<g class=\"edge\" id=\"edge5\"><title>140449680384464-&gt;140449680384744</title>\n",
       "<path d=\"M213,-747.366C213,-739.152 213,-729.658 213,-720.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"216.5,-720.607 213,-710.607 209.5,-720.607 216.5,-720.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140449680359208 -->\n",
       "<g class=\"node\" id=\"node7\"><title>140449680359208</title>\n",
       "<polygon fill=\"none\" points=\"23,-581.5 23,-627.5 403,-627.5 403,-581.5 23,-581.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"126.5\" y=\"-600.8\">max_pooling2d_2: MaxPooling2D</text>\n",
       "<polyline fill=\"none\" points=\"230,-581.5 230,-627.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"257.5\" y=\"-612.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"230,-604.5 285,-604.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"257.5\" y=\"-589.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"285,-581.5 285,-627.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"344\" y=\"-612.3\">(None, 29, 29, 10)</text>\n",
       "<polyline fill=\"none\" points=\"285,-604.5 403,-604.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"344\" y=\"-589.3\">(None, 14, 14, 10)</text>\n",
       "</g>\n",
       "<!-- 140449680384744&#45;&gt;140449680359208 -->\n",
       "<g class=\"edge\" id=\"edge6\"><title>140449680384744-&gt;140449680359208</title>\n",
       "<path d=\"M213,-664.366C213,-656.152 213,-646.658 213,-637.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"216.5,-637.607 213,-627.607 209.5,-637.607 216.5,-637.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140449680385192 -->\n",
       "<g class=\"node\" id=\"node8\"><title>140449680385192</title>\n",
       "<polygon fill=\"none\" points=\"65,-498.5 65,-544.5 361,-544.5 361,-498.5 65,-498.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"126.5\" y=\"-517.8\">conv2d_4: Conv2D</text>\n",
       "<polyline fill=\"none\" points=\"188,-498.5 188,-544.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"215.5\" y=\"-529.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"188,-521.5 243,-521.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"215.5\" y=\"-506.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"243,-498.5 243,-544.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"302\" y=\"-529.3\">(None, 14, 14, 10)</text>\n",
       "<polyline fill=\"none\" points=\"243,-521.5 361,-521.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"302\" y=\"-506.3\">(None, 12, 12, 20)</text>\n",
       "</g>\n",
       "<!-- 140449680359208&#45;&gt;140449680385192 -->\n",
       "<g class=\"edge\" id=\"edge7\"><title>140449680359208-&gt;140449680385192</title>\n",
       "<path d=\"M213,-581.366C213,-573.152 213,-563.658 213,-554.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"216.5,-554.607 213,-544.607 209.5,-554.607 216.5,-554.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140449680385528 -->\n",
       "<g class=\"node\" id=\"node9\"><title>140449680385528</title>\n",
       "<polygon fill=\"none\" points=\"23,-415.5 23,-461.5 403,-461.5 403,-415.5 23,-415.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"126.5\" y=\"-434.8\">max_pooling2d_3: MaxPooling2D</text>\n",
       "<polyline fill=\"none\" points=\"230,-415.5 230,-461.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"257.5\" y=\"-446.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"230,-438.5 285,-438.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"257.5\" y=\"-423.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"285,-415.5 285,-461.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"344\" y=\"-446.3\">(None, 12, 12, 20)</text>\n",
       "<polyline fill=\"none\" points=\"285,-438.5 403,-438.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"344\" y=\"-423.3\">(None, 6, 6, 20)</text>\n",
       "</g>\n",
       "<!-- 140449680385192&#45;&gt;140449680385528 -->\n",
       "<g class=\"edge\" id=\"edge8\"><title>140449680385192-&gt;140449680385528</title>\n",
       "<path d=\"M213,-498.366C213,-490.152 213,-480.658 213,-471.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"216.5,-471.607 213,-461.607 209.5,-471.607 216.5,-471.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140449680385752 -->\n",
       "<g class=\"node\" id=\"node10\"><title>140449680385752</title>\n",
       "<polygon fill=\"none\" points=\"78.5,-332.5 78.5,-378.5 347.5,-378.5 347.5,-332.5 78.5,-332.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"133.5\" y=\"-351.8\">flatten_1: Flatten</text>\n",
       "<polyline fill=\"none\" points=\"188.5,-332.5 188.5,-378.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"216\" y=\"-363.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"188.5,-355.5 243.5,-355.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"216\" y=\"-340.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"243.5,-332.5 243.5,-378.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"295.5\" y=\"-363.3\">(None, 6, 6, 20)</text>\n",
       "<polyline fill=\"none\" points=\"243.5,-355.5 347.5,-355.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"295.5\" y=\"-340.3\">(None, 720)</text>\n",
       "</g>\n",
       "<!-- 140449680385528&#45;&gt;140449680385752 -->\n",
       "<g class=\"edge\" id=\"edge9\"><title>140449680385528-&gt;140449680385752</title>\n",
       "<path d=\"M213,-415.366C213,-407.152 213,-397.658 213,-388.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"216.5,-388.607 213,-378.607 209.5,-388.607 216.5,-388.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140449680385864 -->\n",
       "<g class=\"node\" id=\"node11\"><title>140449680385864</title>\n",
       "<polygon fill=\"none\" points=\"14,-249.5 14,-295.5 412,-295.5 412,-249.5 14,-249.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"144\" y=\"-268.8\">batch_normalization_2: BatchNormalization</text>\n",
       "<polyline fill=\"none\" points=\"274,-249.5 274,-295.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"301.5\" y=\"-280.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"274,-272.5 329,-272.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"301.5\" y=\"-257.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"329,-249.5 329,-295.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"370.5\" y=\"-280.3\">(None, 720)</text>\n",
       "<polyline fill=\"none\" points=\"329,-272.5 412,-272.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"370.5\" y=\"-257.3\">(None, 720)</text>\n",
       "</g>\n",
       "<!-- 140449680385752&#45;&gt;140449680385864 -->\n",
       "<g class=\"edge\" id=\"edge10\"><title>140449680385752-&gt;140449680385864</title>\n",
       "<path d=\"M213,-332.366C213,-324.152 213,-314.658 213,-305.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"216.5,-305.607 213,-295.607 209.5,-305.607 216.5,-305.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140449680386144 -->\n",
       "<g class=\"node\" id=\"node12\"><title>140449680386144</title>\n",
       "<polygon fill=\"none\" points=\"93,-166.5 93,-212.5 333,-212.5 333,-166.5 93,-166.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"144\" y=\"-185.8\">dense_1: Dense</text>\n",
       "<polyline fill=\"none\" points=\"195,-166.5 195,-212.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"222.5\" y=\"-197.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"195,-189.5 250,-189.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"222.5\" y=\"-174.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"250,-166.5 250,-212.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"291.5\" y=\"-197.3\">(None, 720)</text>\n",
       "<polyline fill=\"none\" points=\"250,-189.5 333,-189.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"291.5\" y=\"-174.3\">(None, 128)</text>\n",
       "</g>\n",
       "<!-- 140449680385864&#45;&gt;140449680386144 -->\n",
       "<g class=\"edge\" id=\"edge11\"><title>140449680385864-&gt;140449680386144</title>\n",
       "<path d=\"M213,-249.366C213,-241.152 213,-231.658 213,-222.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"216.5,-222.607 213,-212.607 209.5,-222.607 216.5,-222.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140449680386480 -->\n",
       "<g class=\"node\" id=\"node13\"><title>140449680386480</title>\n",
       "<polygon fill=\"none\" points=\"81.5,-83.5 81.5,-129.5 344.5,-129.5 344.5,-83.5 81.5,-83.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"144\" y=\"-102.8\">dropout_1: Dropout</text>\n",
       "<polyline fill=\"none\" points=\"206.5,-83.5 206.5,-129.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"234\" y=\"-114.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"206.5,-106.5 261.5,-106.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"234\" y=\"-91.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"261.5,-83.5 261.5,-129.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"303\" y=\"-114.3\">(None, 128)</text>\n",
       "<polyline fill=\"none\" points=\"261.5,-106.5 344.5,-106.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"303\" y=\"-91.3\">(None, 128)</text>\n",
       "</g>\n",
       "<!-- 140449680386144&#45;&gt;140449680386480 -->\n",
       "<g class=\"edge\" id=\"edge12\"><title>140449680386144-&gt;140449680386480</title>\n",
       "<path d=\"M213,-166.366C213,-158.152 213,-148.658 213,-139.725\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"216.5,-139.607 213,-129.607 209.5,-139.607 216.5,-139.607\" stroke=\"black\"/>\n",
       "</g>\n",
       "<!-- 140449680386536 -->\n",
       "<g class=\"node\" id=\"node14\"><title>140449680386536</title>\n",
       "<polygon fill=\"none\" points=\"91.5,-0.5 91.5,-46.5 334.5,-46.5 334.5,-0.5 91.5,-0.5\" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"144\" y=\"-19.8\">OutputY: Dense</text>\n",
       "<polyline fill=\"none\" points=\"196.5,-0.5 196.5,-46.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"224\" y=\"-31.3\">input:</text>\n",
       "<polyline fill=\"none\" points=\"196.5,-23.5 251.5,-23.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"224\" y=\"-8.3\">output:</text>\n",
       "<polyline fill=\"none\" points=\"251.5,-0.5 251.5,-46.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"293\" y=\"-31.3\">(None, 128)</text>\n",
       "<polyline fill=\"none\" points=\"251.5,-23.5 334.5,-23.5 \" stroke=\"black\"/>\n",
       "<text font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"293\" y=\"-8.3\">(None, 27)</text>\n",
       "</g>\n",
       "<!-- 140449680386480&#45;&gt;140449680386536 -->\n",
       "<g class=\"edge\" id=\"edge13\"><title>140449680386480-&gt;140449680386536</title>\n",
       "<path d=\"M213,-83.3664C213,-75.1516 213,-65.6579 213,-56.7252\" fill=\"none\" stroke=\"black\"/>\n",
       "<polygon fill=\"black\" points=\"216.5,-56.6068 213,-46.6068 209.5,-56.6069 216.5,-56.6068\" stroke=\"black\"/>\n",
       "</g>\n",
       "</g>\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.SVG object>"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from keras.utils.vis_utils import model_to_dot\n",
    "from IPython.display import SVG\n",
    "\n",
    "SVG(model_to_dot(model, show_shapes=True).create(prog='dot', format='svg'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 测试模型效率，默认测试运行1000次所用时间"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# def testPredict(epochs=1000):\n",
    "#     for i in range(epochs):\n",
    "#         test = np.random.random((1, 64, 64, 3)).astype('float32')\n",
    "#         model.predict(test)\n",
    "# %time testPredict()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# x = np.random.random((1, 64, 64, 3)).astype('float32')\n",
    "# y = model.predict(x)\n",
    "# y.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 开始导入数据\n",
    "\n",
    "**如果数据量庞大，可能需要比较长的时间导入，需要耐心等待！**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Class: 0: 100%|████████████████████████████████| 10/10 [00:00<00:00, 552.33it/s]\n",
      "Class: 1: 100%|█████████████████████████████████| 4/4 [00:00<00:00, 1849.95it/s]\n",
      "Class: 2: 100%|███████████████████████████| 3606/3606 [00:01<00:00, 2671.08it/s]\n",
      "Class: 3: 100%|█████████████████████████████████| 2/2 [00:00<00:00, 1827.98it/s]\n",
      "Class: 4: 100%|█████████████████████████████| 205/205 [00:00<00:00, 2467.66it/s]\n",
      "Class: 5: 100%|█████████████████████████████| 106/106 [00:00<00:00, 2151.59it/s]\n",
      "Class: 6: 100%|█████████████████████████████████| 8/8 [00:00<00:00, 2084.00it/s]\n",
      "Class: 7: 100%|█████████████████████████████████| 2/2 [00:00<00:00, 1654.56it/s]\n",
      "Class: 8: 100%|███████████████████████████████| 20/20 [00:00<00:00, 2433.95it/s]\n",
      "Class: 9: 100%|█████████████████████████████| 163/163 [00:00<00:00, 1718.95it/s]\n",
      "Class: 10: 100%|████████████████████████████| 423/423 [00:00<00:00, 2594.88it/s]\n",
      "Class: 11: 100%|██████████████████████████████| 16/16 [00:00<00:00, 2858.74it/s]\n",
      "Class: 12: 100%|████████████████████████████| 181/181 [00:00<00:00, 2514.77it/s]\n",
      "Class: 13: 100%|██████████████████████████████████| 1/1 [00:00<00:00, 76.92it/s]\n",
      "Class: 14: 100%|████████████████████████████████| 3/3 [00:00<00:00, 1816.76it/s]\n",
      "Class: 15: 100%|████████████████████████████| 101/101 [00:00<00:00, 2381.31it/s]\n",
      "Class: 16: 100%|████████████████████████████████| 7/7 [00:00<00:00, 2495.55it/s]\n",
      "Class: 17: 100%|██████████████████████████████| 11/11 [00:00<00:00, 2183.81it/s]\n",
      "Class: 18: 100%|██████████████████████████████| 15/15 [00:00<00:00, 1435.29it/s]\n",
      "Class: 19: 100%|████████████████████████████████| 6/6 [00:00<00:00, 1178.62it/s]\n",
      "Class: 20: 100%|████████████████████████████████| 1/1 [00:00<00:00, 1050.68it/s]\n",
      "Class: 21: 100%|████████████████████████████████| 3/3 [00:00<00:00, 1522.43it/s]\n",
      "Class: 22: 100%|██████████████████████████████| 13/13 [00:00<00:00, 2798.21it/s]\n",
      "Class: 23: 100%|████████████████████████████████| 3/3 [00:00<00:00, 1755.92it/s]\n",
      "Class: 24: 100%|██████████████████████████████| 50/50 [00:00<00:00, 2734.73it/s]\n",
      "Class: 25: 100%|██████████████████████████████| 23/23 [00:00<00:00, 1831.12it/s]\n",
      "Class: 26: 100%|██████████████████████████████| 18/18 [00:00<00:00, 1794.91it/s]\n"
     ]
    }
   ],
   "source": [
    "datasets = readDatasets(labels)\n",
    "datas = getDatas(datasets, dtype='float32')\n",
    "targets = getTargets(datasets)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "train_X = (1 - datas / 255).reshape(-1, 64, 64, 3)\n",
    "train_Y = keras.utils.to_categorical(targets, num_classes=classes_num)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 开始训练神经网络模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "if USE_TRAINING:\n",
    "    model.fit(train_X, train_Y, batch_size=batch_size, epochs=epochs_num)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 保存训练好的模型\n",
    "\n",
    "**PS：**可能需要安装 h5py 库，可以通过命令：`pip install h5py` 进行安装。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "if USE_TRAINING:\n",
    "    model.save(model_filename)\n",
    "    with open('labels.pkl', 'wb') as f:\n",
    "        pickle.dump(labels, f)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 验证人工神经网络在训练集的效果\n",
    "\n",
    "其返回结果为[代价值，准确率]，**如果数据量足够，最好使用测试集进行训练！**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "if os.path.isfile(model_filename):\n",
    "    model = keras.models.load_model(model_filename)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5001/5001 [==============================] - 15s 3ms/step\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[0.0009054067593104757, 0.9998000399920016]"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.evaluate(train_X, train_Y, batch_size=batch_size)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 返回错误样本信息\n",
    "\n",
    "此处的“错误样本”指模型判断失误的样本。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[4891]"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "predict_Y = model.predict(train_X, batch_size=batch_size)\n",
    "\n",
    "predict_targets = (predict_Y == predict_Y.max(axis=1).reshape(-1, 1)).dot(np.arange(classes_num))\n",
    "errors_id = np.where(targets != predict_targets)[0].tolist()\n",
    "errors_id[:20]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 可视化错误样本"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# %matplotlib inline\n",
    "# import matplotlib.pyplot as plt\n",
    "\n",
    "# errors_count = len(errors_id)\n",
    "# col_num = 5\n",
    "# row_num = errors_count // col_num + 1\n",
    "# plt.figure(figsize=(8, 4))\n",
    "# for i in range(errors_count):\n",
    "#     error_id = errors_id[i]\n",
    "#     image = datas[error_id].astype('uint8')\n",
    "#     plt.subplot(row_num, col_num, i + 1)\n",
    "#     plt.title('Data ID: {}'.format(error_id))\n",
    "#     plt.imshow(image)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
